#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from flask import Flask, render_template, request, jsonify, send_file, redirect, url_for, flash
from werkzeug.utils import secure_filename
import os
import json
import asyncio
import threading
import time
from datetime import datetime
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from prompt_manager import PromptManager
from models import PromptItem, Category

app = Flask(__name__)
app.secret_key = 'your-secret-key-here'  # 在生产环境中应该使用更安全的密钥

# 配置
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

# 确保上传目录存在
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

# 初始化提示词管理器
manager = PromptManager()

# 热重载配置
class HotReloadHandler(FileSystemEventHandler):
    """文件变化监听器"""
    def __init__(self, callback):
        self.callback = callback
        self.last_modified = {}
        
    def on_modified(self, event):
        if event.is_directory:
            return
            
        # 只监听Python文件和模板文件
        if not (event.src_path.endswith('.py') or event.src_path.endswith('.html')):
            return
            
        # 防止重复触发
        current_time = time.time()
        if event.src_path in self.last_modified:
            if current_time - self.last_modified[event.src_path] < 1:
                return
                
        self.last_modified[event.src_path] = current_time
        print(f"检测到文件变化: {event.src_path}")
        
        # 异步执行回调
        threading.Thread(target=self.callback, daemon=True).start()

def reload_manager():
    """重新加载管理器"""
    global manager
    try:
        print("正在重新加载数据...")
        manager = PromptManager()
        print("数据重新加载完成")
    except Exception as e:
        print(f"重新加载失败: {e}")

# 启动文件监听
def start_file_watcher():
    """启动文件监听器"""
    event_handler = HotReloadHandler(reload_manager)
    observer = Observer()
    
    # 监听当前目录和数据目录
    observer.schedule(event_handler, '.', recursive=True)
    observer.schedule(event_handler, 'data', recursive=True)
    observer.schedule(event_handler, 'templates', recursive=True)
    
    observer.start()
    print("文件监听器已启动")
    return observer

# 异步任务管理
class AsyncTaskManager:
    """异步任务管理器"""
    def __init__(self):
        self.tasks = {}
        self.task_counter = 0
        
    def create_task(self, coro, task_name=None):
        """创建异步任务"""
        self.task_counter += 1
        task_id = f"task_{self.task_counter}"
        
        if task_name:
            task_id = f"{task_name}_{self.task_counter}"
            
        # 在新线程中运行异步任务
        def run_async():
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            try:
                result = loop.run_until_complete(coro)
                self.tasks[task_id] = {'status': 'completed', 'result': result}
            except Exception as e:
                self.tasks[task_id] = {'status': 'failed', 'error': str(e)}
            finally:
                loop.close()
                
        thread = threading.Thread(target=run_async, daemon=True)
        thread.start()
        
        self.tasks[task_id] = {'status': 'running', 'thread': thread}
        return task_id
        
    def get_task_status(self, task_id):
        """获取任务状态"""
        return self.tasks.get(task_id, {'status': 'not_found'})
        
    def list_tasks(self):
        """列出所有任务"""
        return {tid: {k: v for k, v in task.items() if k != 'thread'} 
                for tid, task in self.tasks.items()}

# 初始化异步任务管理器
task_manager = AsyncTaskManager()

@app.route('/')
def index():
    """主页"""
    stats = manager.get_statistics()
    return render_template('index.html', stats=stats)

@app.route('/async-demo')
def async_demo():
    """异步功能演示页面"""
    return render_template('async_demo.html')

@app.route('/prompts')
def prompts():
    """提示词列表页面"""
    # 获取查询参数
    query = request.args.get('query', '')
    category_id = request.args.get('category', '')
    tags = request.args.get('tags', '')
    enabled_only = request.args.get('enabled_only') == 'true'
    disabled_only = request.args.get('disabled_only') == 'true'
    
    # 搜索提示词
    tag_list = [tag.strip() for tag in tags.split(',') if tag.strip()] if tags else None
    prompts = manager.search_prompts(
        query=query,
        category_id=category_id if category_id else None,
        tags=tag_list,
        enabled_only=enabled_only,
        disabled_only=disabled_only
    )
    
    # 获取分类和标签
    categories = manager.get_all_categories()
    all_tags = manager.get_all_tags()
    
    return render_template('prompts.html', 
                         prompts=prompts, 
                         categories=categories,
                         all_tags=all_tags,
                         current_query=query,
                         current_category=category_id,
                         current_tags=tags,
                         enabled_only=enabled_only,
                         disabled_only=disabled_only)

@app.route('/prompts/add', methods=['GET', 'POST'])
def add_prompt():
    """添加提示词"""
    if request.method == 'POST':
        title = request.form.get('title', '').strip()
        content = request.form.get('content', '').strip()
        tags_str = request.form.get('tags', '').strip()
        category_id = request.form.get('category_id', 'default')
        enabled = request.form.get('enabled') == 'on'
        
        if not title or not content:
            flash('标题和内容不能为空', 'error')
            return redirect(url_for('add_prompt'))
        
        # 处理标签
        tags = [tag.strip() for tag in tags_str.split(',') if tag.strip()] if tags_str else []
        
        # 添加提示词
        prompt = manager.add_prompt(
            title=title,
            content=content,
            tags=tags,
            category_id=category_id,
            enabled=enabled
        )
        
        if prompt:
            flash('提示词添加成功', 'success')
            return redirect(url_for('prompts'))
        else:
            flash('提示词添加失败', 'error')
    
    categories = manager.get_all_categories()
    return render_template('add_prompt.html', categories=categories)

@app.route('/prompts/<prompt_id>/edit', methods=['GET', 'POST'])
def edit_prompt(prompt_id):
    """编辑提示词"""
    prompt = manager.get_prompt_by_id(prompt_id)
    if not prompt:
        flash('提示词不存在', 'error')
        return redirect(url_for('prompts'))
    
    if request.method == 'POST':
        title = request.form.get('title', '').strip()
        content = request.form.get('content', '').strip()
        tags_str = request.form.get('tags', '').strip()
        category_id = request.form.get('category_id')
        enabled = request.form.get('enabled') == 'on'
        
        if not title or not content:
            flash('标题和内容不能为空', 'error')
            return render_template('edit_prompt.html', prompt=prompt, categories=manager.get_all_categories())
        
        # 处理标签
        tags = [tag.strip() for tag in tags_str.split(',') if tag.strip()] if tags_str else []
        
        # 更新提示词
        success = manager.update_prompt(
            prompt_id=prompt_id,
            title=title,
            content=content,
            tags=tags,
            category_id=category_id,
            enabled=enabled
        )
        
        if success:
            flash('提示词更新成功', 'success')
            return redirect(url_for('prompts'))
        else:
            flash('提示词更新失败', 'error')
    
    categories = manager.get_all_categories()
    return render_template('edit_prompt.html', prompt=prompt, categories=categories)

@app.route('/prompts/<prompt_id>/delete', methods=['POST'])
def delete_prompt(prompt_id):
    """删除提示词"""
    success = manager.delete_prompt(prompt_id)
    if success:
        flash('提示词删除成功', 'success')
    else:
        flash('提示词删除失败', 'error')
    return redirect(url_for('prompts'))

@app.route('/prompts/<prompt_id>/toggle', methods=['POST'])
def toggle_prompt(prompt_id):
    """切换提示词启用状态"""
    success = manager.toggle_prompt_enabled(prompt_id)
    if success:
        return jsonify({'success': True})
    else:
        return jsonify({'success': False}), 400

@app.route('/prompts/<prompt_id>/use')
def use_prompt(prompt_id):
    """使用提示词页面"""
    prompt = manager.get_prompt_by_id(prompt_id)
    if not prompt:
        flash('提示词不存在', 'error')
        return redirect(url_for('prompts'))
    
    return render_template('use_prompt.html', prompt=prompt)

@app.route('/api/prompts/<prompt_id>/replace', methods=['POST'])
def replace_variables(prompt_id):
    """替换变量API"""
    prompt = manager.get_prompt_by_id(prompt_id)
    if not prompt:
        return jsonify({'error': '提示词不存在'}), 404
    
    variable_values = request.json or {}
    result = prompt.replace_variables(variable_values)
    
    return jsonify({
        'success': True,
        'result': result,
        'variables': prompt.variables or []
    })

@app.route('/categories')
def categories():
    """分类列表页面"""
    categories = manager.get_all_categories()
    
    # 为每个分类添加提示词数量
    for category in categories:
        category.prompt_count = manager.get_prompt_count_by_category(category.id)
    
    return render_template('categories.html', categories=categories)

@app.route('/categories/add', methods=['GET', 'POST'])
def add_category():
    """添加分类"""
    if request.method == 'POST':
        name = request.form.get('name', '').strip()
        description = request.form.get('description', '').strip() or None
        color = request.form.get('color', '').strip() or None
        
        if not name:
            flash('分类名称不能为空', 'error')
            return redirect(url_for('add_category'))
        
        category = manager.add_category(
            name=name,
            description=description,
            color=color
        )
        
        if category:
            flash('分类添加成功', 'success')
            return redirect(url_for('categories'))
        else:
            flash('分类添加失败', 'error')
    
    return render_template('add_category.html')

@app.route('/categories/<category_id>/edit', methods=['GET', 'POST'])
def edit_category(category_id):
    """编辑分类"""
    category = manager.get_category_by_id(category_id)
    if not category:
        flash('分类不存在', 'error')
        return redirect(url_for('categories'))
    
    if request.method == 'POST':
        name = request.form.get('name', '').strip()
        description = request.form.get('description', '').strip() or None
        color = request.form.get('color', '').strip() or None
        enabled = request.form.get('enabled') == 'on'
        
        if not name:
            flash('分类名称不能为空', 'error')
            return render_template('edit_category.html', category=category)
        
        success = manager.update_category(
            category_id=category_id,
            name=name,
            description=description,
            color=color,
            enabled=enabled
        )
        
        if success:
            flash('分类更新成功', 'success')
            return redirect(url_for('categories'))
        else:
            flash('分类更新失败', 'error')
    
    return render_template('edit_category.html', category=category)

@app.route('/categories/<category_id>/delete', methods=['POST'])
def delete_category(category_id):
    """删除分类"""
    if category_id == 'default':
        flash('不能删除默认分类', 'error')
        return redirect(url_for('categories'))
    
    success = manager.delete_category(category_id)
    if success:
        flash('分类删除成功', 'success')
    else:
        flash('分类删除失败', 'error')
    return redirect(url_for('categories'))

@app.route('/import', methods=['GET', 'POST'])
def import_prompts():
    if request.method == 'GET':
        categories = manager.get_all_categories()
        return render_template('import.html', categories=categories)
    
    # 获取导入模式
    import_mode = request.form.get('import_mode', 'file')
    
    # 处理JSON内容导入（编辑后的内容）
    if import_mode == 'json_content':
        title = request.form.get('title', '').strip()
        json_content = request.form.get('json_content', '').strip()
        
        # 基本验证
        if not title:
            flash('标题不能为空', 'error')
            return redirect(url_for('import_prompts'))
        
        if not json_content:
            flash('JSON内容不能为空', 'error')
            return redirect(url_for('import_prompts'))
        
        try:
            # 验证JSON格式
            import json
            json_data = json.loads(json_content)
            
            # 格式化JSON内容
            formatted_content = json.dumps(json_data, ensure_ascii=False, indent=2)
            
            # 添加提示词
            success = manager.add_prompt(
                title=title,
                content=formatted_content,
                category_id='default',
                tags=['JSON导入', '已编辑'],  # 添加"已编辑"标签表示内容被修改过
                enabled=True
            )
            
            if success:
                flash(f'编辑后的JSON数据 "{title}" 导入成功！', 'success')
            else:
                flash('导入失败，请检查输入内容', 'error')
                
        except json.JSONDecodeError as e:
            # JSON格式错误
            error_msg = f'JSON格式错误：第{e.lineno}行，第{e.colno}列 - {e.msg}'
            flash(error_msg, 'error')
        except Exception as e:
            flash(f'导入失败：{str(e)}', 'error')
        
        return redirect(url_for('import_prompts'))
    
    # 处理文件上传导入（原始文件内容）
    elif import_mode == 'file' and 'file' in request.files:
        file = request.files['file']
        if file.filename == '':
            flash('请选择一个JSON文件', 'error')
            return redirect(url_for('import_prompts'))
        
        # 检查文件扩展名
        if not file.filename.lower().endswith('.json'):
            flash('只支持JSON格式文件', 'error')
            return redirect(url_for('import_prompts'))
        
        # 保存上传的文件
        filename = secure_filename(file.filename)
        filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        file.save(filepath)
        
        try:
            # 读取JSON文件内容
            content = None
            try:
                with open(filepath, 'r', encoding='utf-8') as f:
                    content = f.read()
            except UnicodeDecodeError:
                # 如果UTF-8解码失败，尝试GBK
                with open(filepath, 'r', encoding='gbk') as f:
                    content = f.read()
            
            # 验证JSON格式
            try:
                import json
                json_data = json.loads(content)
                
                # 格式化JSON内容
                formatted_content = json.dumps(json_data, ensure_ascii=False, indent=2)
                
                # 使用用户输入的标题或文件名（不含扩展名）作为标题
                title = request.form.get('title', '').strip()
                if not title:
                    title = os.path.splitext(filename)[0]
                
                # 创建新的提示词，内容为格式化后的JSON
                success = manager.add_prompt(
                    title=title,
                    content=formatted_content,
                    category_id='default',  # 默认分类
                    tags=['JSON导入'],  # 添加JSON导入标签
                    enabled=True
                )
                
                if success:
                    flash(f'JSON文件 "{filename}" 导入成功！', 'success')
                else:
                    flash('导入失败，请检查文件内容', 'error')
                    
            except json.JSONDecodeError as e:
                # JSON格式错误
                error_msg = f'JSON格式错误：第{e.lineno}行，第{e.colno}列 - {e.msg}'
                flash(error_msg, 'error')
                
        except Exception as e:
            flash(f'导入失败：{str(e)}', 'error')
        finally:
            # 删除临时文件
            if os.path.exists(filepath):
                os.remove(filepath)
        
        return redirect(url_for('import_prompts'))
    
    else:
        flash('请选择文件或输入JSON内容', 'error')
        return redirect(url_for('import_prompts'))

@app.route('/export')
def export_data():
    """导出数据"""
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"prompts_export_{timestamp}.json"
    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    
    success = manager.export_prompts(filepath)
    if success:
        return send_file(filepath, as_attachment=True, download_name=filename)
    else:
        flash('数据导出失败', 'error')
        return redirect(url_for('index'))

@app.route('/backup')
def backup_data():
    """备份数据"""
    backup_file = manager.backup_data()
    if backup_file:
        flash(f'数据备份成功: {backup_file}', 'success')
    else:
        flash('数据备份失败', 'error')
    return redirect(url_for('index'))

@app.route('/stats')
def stats():
    """统计页面"""
    stats = manager.get_statistics()
    return render_template('stats.html', stats=stats)

@app.route('/api/search')
def api_search():
    """搜索API"""
    query = request.args.get('q', '')
    category_id = request.args.get('category', '')
    limit = int(request.args.get('limit', 10))
    
    prompts = manager.search_prompts(
        query=query,
        category_id=category_id if category_id else None,
        enabled_only=True
    )
    
    # 限制结果数量
    prompts = prompts[:limit]
    
    results = []
    for prompt in prompts:
        category = manager.get_category_by_id(prompt.category_id)
        results.append({
            'id': prompt.id,
            'title': prompt.title,
            'content': prompt.content[:100] + '...' if len(prompt.content) > 100 else prompt.content,
            'tags': prompt.tags,
            'category': category.name if category else '未知',
            'variables': prompt.variables or []
        })
    
    return jsonify({
        'success': True,
        'results': results,
        'total': len(results)
    })

# 异步API路由
@app.route('/api/async/backup', methods=['POST'])
def async_backup():
    """异步备份数据"""
    async def backup_task():
        await asyncio.sleep(0.1)  # 模拟异步操作
        try:
            backup_file = manager.backup_data()
            return {'success': True, 'backup_file': backup_file}
        except Exception as e:
            return {'success': False, 'error': str(e)}
    
    task_id = task_manager.create_task(backup_task(), 'backup')
    return jsonify({
        'success': True,
        'task_id': task_id,
        'message': '备份任务已启动'
    })

@app.route('/api/async/import', methods=['POST'])
def async_import():
    """异步导入数据"""
    data = request.get_json()
    if not data or 'content' not in data:
        return jsonify({'success': False, 'error': '缺少导入内容'}), 400
    
    async def import_task():
        await asyncio.sleep(0.1)  # 模拟异步操作
        try:
            # 这里可以添加大文件导入的异步处理逻辑
            content = data['content']
            title = data.get('title', '异步导入数据')
            
            # 模拟处理时间
            await asyncio.sleep(2)
            
            # 实际导入逻辑
            prompt = PromptItem(
                title=title,
                content=content,
                tags=['异步导入'],
                category_id=None
            )
            manager.add_prompt(prompt)
            
            return {'success': True, 'message': '数据导入成功'}
        except Exception as e:
            return {'success': False, 'error': str(e)}
    
    task_id = task_manager.create_task(import_task(), 'import')
    return jsonify({
        'success': True,
        'task_id': task_id,
        'message': '导入任务已启动'
    })

@app.route('/api/async/search', methods=['POST'])
def async_search():
    """异步搜索"""
    data = request.get_json()
    query = data.get('query', '') if data else ''
    
    async def search_task():
        await asyncio.sleep(0.1)  # 模拟异步操作
        try:
            # 模拟复杂搜索处理
            await asyncio.sleep(1)
            
            prompts = manager.search_prompts(query=query)
            results = []
            
            for prompt in prompts:
                category = manager.get_category(prompt.category_id) if prompt.category_id else None
                results.append({
                    'id': prompt.id,
                    'title': prompt.title,
                    'content': prompt.content[:200] + '...' if len(prompt.content) > 200 else prompt.content,
                    'tags': prompt.tags,
                    'category': category.name if category else '未知',
                    'created_at': prompt.created_at.isoformat() if prompt.created_at else None
                })
            
            return {'success': True, 'results': results, 'total': len(results)}
        except Exception as e:
            return {'success': False, 'error': str(e)}
    
    task_id = task_manager.create_task(search_task(), 'search')
    return jsonify({
        'success': True,
        'task_id': task_id,
        'message': '搜索任务已启动'
    })

@app.route('/api/tasks/<task_id>')
def get_task_status(task_id):
    """获取任务状态"""
    status = task_manager.get_task_status(task_id)
    return jsonify(status)

@app.route('/api/tasks')
def list_tasks():
    """列出所有任务"""
    tasks = task_manager.list_tasks()
    return jsonify(tasks)

@app.route('/api/reload', methods=['POST'])
def manual_reload():
    """手动重新加载数据"""
    try:
        reload_manager()
        return jsonify({'success': True, 'message': '数据重新加载成功'})
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)}), 500

@app.errorhandler(404)
def not_found(error):
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_error(error):
    return render_template('500.html'), 500

def create_app(enable_hot_reload=True):
    """创建Flask应用"""
    if enable_hot_reload:
        observer = start_file_watcher()
        
        # 注册应用关闭时的清理函数
        import atexit
        atexit.register(lambda: observer.stop())
    
    return app

if __name__ == '__main__':
    # 启动热重载
    observer = start_file_watcher()
    
    try:
        print("启动Flask应用 (支持热重载)...")
        print("访问地址: http://localhost:5000")
        print("按 Ctrl+C 停止服务")
        app.run(debug=True, host='0.0.0.0', port=5000, use_reloader=False)  # 禁用Flask自带的重载器
    except KeyboardInterrupt:
        print("\n正在停止服务...")
        observer.stop()
        observer.join()
        print("服务已停止")